STM8 Timer 2 and 3

 Timer Signal generieren

Die Grundeinstellungen um einen Zähler zu konfigurieren bestehen aus folgenden Schritten.

Einstellung des Zählertaktes

Der Zählertakt errechnet sich aus dem Systemtakt (fmaster), der üblicherweise vom externen Quarz (fHSE) oder vom internen „RC-Oszillator“ (fHSI, 16MHz) erzeugt wird. Die Auswahl der Taktquelle erfolgt im Register CLK_CMSR, welches nicht Teil der Timer-Register ist.

Nach einem Reset started der STM8 mit der internen fHSI Frequenz die zusätzlich noch durch 8 geteilt wird, also mit 2 MHz. Im Register CLK_CKDIVR müssen die beiden Bits HSIDIV[1..0] auf „0x00b“ gesetzt werden um den Clock Teiler von „/8“ auf „/1“ zu stellen, danach ist fMaster = 16 MHz. Details über die Einstellung der Master-Clock finden sich im „STM8 Referenz Manual“ von STMicroelectronics. Für den weiteren Verlauf gehen wir jetzt von einem 16MHz Takt aus.

Der Takt (fmaster) wird über den jeweiligen Prescaler der Zähler (TIMn_PSCR) heruntergeteilt. Der resultierende Takt ergibt sich nach der Formel:

fCK_CNT = fCK_PSC/2(PSCR[3..0])

fCK_PSC ist die Eingangsfrequenz am Prescaler und entspricht dem Systemtakt fMaster, daher kann in oben angegebener Formel fCK_PSC durch fMaster ersetzt werden.

Beispiel:

Zur Erzeugung einer Zählerfrequenz von 1 MHz muß die fMaster Frequenz durch 16 geteilt werden. Daher gilt:

fCK_CNT = fMaster / 2(x) X = fMaster/fCK_CNT

X = 16 MHz/ 1 MHz = 4

In das TIMn_PSCR muß deshalb der Wert “4” (0x0000.0100b) eingetragen werden. Damit wird der Zähler mit einer Frequenz von 1 MHz getaktet.

Hinweis: Es ist hier zu beachten dass der PSCR-Wert nicht frei als 16-Bit Wert konfiguriert werden kann sondern lediglich eine Auswahl von 16 verschiedenen Teilerverhältnissen zur Basis „2“ in den niedrigwertigen 4 Bit des TIMn_PSCR Registers bietet. Eine fMaster Frequenz von 8 MHz würde deshalb keine Ausgangsfrequenz von exakt 1 MHz ermöglichen, da der Prescaler nicht durch 3 teilen kann.

Einstellung der Ausgangsfrequenz

Wie wir gesehen haben wird der Zähler bei einem Gleichstand des 16-Bit Zählers mit dem Auto-Reload Register (TIMn_ARR) zurückgesetzt. Daher kann über den Wert im Auto-Reload Register die Ausgangsfrequenz des Zählers eingestellt werden.

Beispiel:

Wird eine Ausgangsfrequenz von 1 kHz ( entspricht 1ms) gewünscht so muß der gewählte Zählertakt (1 MHz) durch 1000 geteilt werden. Für das Auto-Reload Register das den Zählerstand für einen Reset des Zählers beinhaltet bedeutet dies einen Wert von ebenfalls „1000“ (0x03E8h).

Erzeugung eines Timer-Interrupts

Bei einem „Update“ (hier: Reset des Zählers durch das Erreichen des ARR-Wertes) kann ein Interrupt ausgelöst werden. Dazu muß das Bit UIE (Update Interrupt Enable) im TIMn_IER gesetzt werden.

Aus der „Interrupt Vector Map“ im Datenblatt (STM8S105) entnehmen wir folgende Vektoren:

Update/Overflow Interrupts
Timer-Nr. Interrupt-Source Vektor-Adresse Interrupt-Nr.
Timer 2 TIM2 Update/Overflow 0x00.803Ch irq13
Timer 3 TIM3 Update/Overflow 0x00.8044h irq15

Mit den, in den bisherigen Beispiel benutzten Werten würde je nach verwendetem Zähler (Timer2 oder Timer3) jede Millisekunde ein Interrupt erzeugt der für einen geordneten Zeitablauf innerhalb einer Software verwendet werden kann.

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

; INTERRUPT Vektoren

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

      segment 'vectit'

      dc.l {$82000000+main}                        ; reset

      dc.l {$82000000+NonHandledInterrupt}         ; trap

      dc.l {$82000000+NonHandledInterrupt}         ; irq0 TLI

      dc.l {$82000000+NonHandledInterrupt}         ; irq1 AWU

      dc.l {$82000000+NonHandledInterrupt}         ; irq2 CLK

      dc.l {$82000000+NonHandledInterrupt}         ; irq3 Port A

      dc.l {$82000000+NonHandledInterrupt}         ; irq4 Port B

      dc.l {$82000000+NonHandledInterrupt}         ; irq5 Port C

      dc.l {$82000000+NonHandledInterrupt}         ; irq6 Port D

      dc.l {$82000000+NonHandledInterrupt}         ; irq7 Port E

      dc.l {$82000000+NonHandledInterrupt}         ; irq8 CAN RX

      dc.l {$82000000+NonHandledInterrupt}         ; irq9 CAN TX

      dc.l {$82000000+NonHandledInterrupt}         ; irq10 SPI

      dc.l {$82000000+NonHandledInterrupt}         ; irq11 TIM1 update (generate 100 ms)

      dc.l {$82000000+NonHandledInterrupt}         ; irq12 TIM1 CapComp

      dc.l {$82000000+Timer2Interrupt}             ; irq13 TIM2 update

      dc.l {$82000000+NonHandledInterrupt}         ; irq14 TIM2 CapComp

      dc.l {$82000000+Timer3Interrupt}             ; irq15 TIM3 update

      dc.l {$82000000+NonHandledInterrupt}         ; irq16 TIM3 CapComp

      dc.l {$82000000+NonHandledInterrupt}         ; irq17 UART2 TX

      dc.l {$82000000+NonHandledInterrupt}         ; irq18 UART2 RX

      dc.l {$82000000+NonHandledInterrupt}         ; irq19 I2C

      dc.l {$82000000+NonHandledInterrupt}         ; irq20 LIN-UART TX

      dc.l {$82000000+NonHandledInterrupt}         ; irq21 LIN-UART RX

      dc.l {$82000000+NonHandledInterrupt}         ; irq22 ADC end of conversion

      dc.l {$82000000+NonHandledInterrupt}         ; irq23 TIM4 update

      dc.l {$82000000+NonHandledInterrupt}         ; irq24 Flash

      dc.l {$82000000+NonHandledInterrupt}         ; irq25 TIM5 update

      dc.l {$82000000+NonHandledInterrupt}         ; irq26 TIM5 CapComp

      dc.l {$82000000+NonHandledInterrupt}         ; irq27 SPI2

      dc.l {$82000000+NonHandledInterrupt}         ; irq28 ADC2

      dc.l {$82000000+NonHandledInterrupt}         ; irq29 Port G & K

Beispiel:

Nachfolgend eine Initialisierungs-Sequenz für den Timer 2 um alle 100 ms einen Interrupt zu erzeugen. Voraussetzung ist ein 16 MHz Eingangstakt für den Prescalers, der durch die Initialisierung diesen Takt durch 512 teilt und damit eine Ausgangsfrequenz von 31,250 kHz erzeugt (entspricht: 32µsec).

Die 31,250 kHz treiben nun den Zähler der mit dem ARR-Register verglichen wird. Um einen Interrupt alle 100msec zu erzeugen muß das TIM2_ARR Register mit einem Wert von 3125 (0x0C35h) geladen werden.

      ; Init Timer 2 (Timebase @ 100ms) ......................

         ld  A,#$09                   ; Setze Prescaler auf 1/512

         ld  TIM2_PSCR,A              ; Lade den Prescaler Wert in TIM2_PSCR

         ld  A,#$0C                   ; Setze TIM2_ARRH auf 0x0Ch

         ld  TIM2_ARRH,A              ; Lade Auto-Preload High-Byte mit 0x0Ch

         ld  A,#$35                   ; Setze TIM2_ARRL auf 0x35h

         ld  TIM2_ARRL,A              ; Lade Auto Preload Low-Byte mit 0x35h

                                      ; TIM2_ARR = 0x0C35h = 3125 (Teile durch 3125)

         ld  A,#$85                   ; Freigabe: ARPE, kein UDIS, CEN (Zähler)

         ld  TIM2_CR1,A               ; Lade TIM2_CR1 mit 0x85h

         ld  A,#$01                   ; Freigabe des Update Interrupts von Timer 2

         ld  TIM2_IER,A               ; Lade TIM2_IER mit 0x01h

Schliesslich wird im TIM2_CR1 der Zähler und anschliessend im TIM2_IER der Interrupt für einen Zähler-Update freigegeben. Ab diesem Zeitpunkt wird der TIMER 2 Update Interrupt (irq13) erzeugt.

Copyright Notiz

Dieses Dokument sowie dessen Inhalt, insbesondere Texte, Fotografien und Grafiken, unterliegt dem Copyright (© 2017) und sind nur mit einer schriftlicher Zustimmung des Autors, Dipl.Ing.(FH) Franz Henkel zur vollständigen oder auszugsweisen Weiterverwendung in Form einer gedruckten oder elektronischen Kopie oder Replikation bzw. einer vollständigen oder auszugsweisen Bereitstellung des Inhalts in schriftlicher oder elektronischer Form, zu verwenden.